package Utils;

import com.sun.jdi.IntegerType;
import midend.IR_Type.*;
import midend.IR_Value.*;

import java.util.*;

public class LibFunc {

    public static HashSet<IR_FuncBlock> libFunc = new HashSet<>();
    private static IR_Type i32Type = IR_Int32.IR_Int32();
    private static IR_Type floatType = IR_Float.IR_Float();
    private static IR_Type voidType = IR_Void.IR_Void();

    private static IR_Func getIntType = new IR_Func(i32Type, new ArrayList<>());
    private static IR_FuncBlock GETINT = new IR_FuncBlock(getIntType,"@getint");

    private static IR_Func getchType = new IR_Func(i32Type, new ArrayList<>());
    private static IR_FuncBlock GETCH = new IR_FuncBlock(getchType, "@getch");

    private static IR_Func getFloatType = new IR_Func(floatType,new ArrayList<>());
    private static IR_FuncBlock GETFLOAT = new IR_FuncBlock(getFloatType, "@getfloat");

    private static List<IR_Type> getArrayTypeParams = Arrays.asList(new IR_Pointer(i32Type));
    private static IR_Func getArrayType = new IR_Func(i32Type,getArrayTypeParams);
    private static IR_FuncBlock GETARRAY = new IR_FuncBlock(getArrayType, "@getarray");

    private static List<IR_Type> getFArrayTypeParams = Arrays.asList(new IR_Pointer(floatType));
    private static IR_Func getFArrayType = new IR_Func(i32Type, getFArrayTypeParams);
    private static IR_FuncBlock GETFARRAY = new IR_FuncBlock(getFArrayType, "@getfarray");

    private static List<IR_Type> putIntTypeParams = Arrays.asList(i32Type);
    private static IR_Func putIntType = new IR_Func(voidType, putIntTypeParams);
    private static IR_FuncBlock PUTINT = new IR_FuncBlock(putIntType, "@putint");

    private static List<IR_Type> putChTypeParams = Arrays.asList(i32Type);
    private static IR_Func putChType = new IR_Func(voidType, putChTypeParams);
    private static IR_FuncBlock PUTCH = new IR_FuncBlock(putChType, "@putch");

    private static List<IR_Type> putFloatTypeParams = Arrays.asList(floatType);
    private static IR_Func putFloatType = new IR_Func(voidType, putFloatTypeParams);
    private static IR_FuncBlock PUTFLOAT = new IR_FuncBlock(putFloatType, "@putfloat");

    private static List<IR_Type> putArrayTypeParams = Arrays.asList(i32Type,new IR_Pointer(i32Type));
    private static IR_Func putArrayType = new IR_Func(voidType, putArrayTypeParams);
    private static IR_FuncBlock PUTARRAY = new IR_FuncBlock(putArrayType, "@putarray");

    private static List<IR_Type> putFArrayTypeParams = Arrays.asList(i32Type,new IR_Pointer(floatType));
    private static IR_Func putFArrayType = new IR_Func(voidType, putFArrayTypeParams);
    private static IR_FuncBlock PUTFARRAY = new IR_FuncBlock(putFArrayType, "@putfarray");

    private static List<IR_Type> putFTypeParams = Arrays.asList(new IR_Pointer(i32Type));
    private static IR_Func putFType = new IR_Func(voidType, putFTypeParams);
    private static IR_FuncBlock PUTF = new IR_FuncBlock(putFType, "@putf");

    private static List<IR_Type> startTimeTypeParams = new ArrayList<>();
    private static IR_Func startTimeType = new IR_Func(voidType, startTimeTypeParams);
    private static IR_FuncBlock STARTTIME = new IR_FuncBlock(startTimeType, "@_sysy_starttime");

    private static List<IR_Type> endTimeTypeParams = new ArrayList<>();
    private static IR_Func endTimeType = new IR_Func(voidType, endTimeTypeParams);
    private static IR_FuncBlock STOPTIME = new IR_FuncBlock(endTimeType, "@_sysy_stoptime");

    //"declare void @memset(i32*, i32, i32)
    private static List<IR_Type> memsetParams =  Arrays.asList(new IR_Pointer(i32Type),i32Type,i32Type);
    private static IR_Func memsetType = new IR_Func(voidType, memsetParams);
    private static IR_FuncBlock MEMSET = new IR_FuncBlock(memsetType, "@memset");

    public static IR_FuncBlock findFunc(String ident) {
        return switch (ident) {
            case "getint" -> GETINT;
            case "getch" -> GETCH;
            case "getfloat" -> GETFLOAT;
            case "getarray" -> GETARRAY;
            case "getfarray" -> GETFARRAY;
            case "putint" -> PUTINT;
            case "putch" -> PUTCH;
            case "putfloat" -> PUTFLOAT;
            case "putarray" -> PUTARRAY;
            case "putfarray" -> PUTFARRAY;
            case "putf" -> PUTF;
            case "starttime" -> STARTTIME;
            case "stoptime" -> STOPTIME;
            case "memset" -> MEMSET;
            default -> null;
        };
    }

    public static void initLib() {

        libFunc.add(GETINT);
        GETINT.setSideEffect();

        libFunc.add(GETCH);
        GETCH.setSideEffect();

        libFunc.add(GETFLOAT);
        GETFLOAT.setSideEffect();

        libFunc.add(PUTINT);
        PUTINT.setSideEffect();

        libFunc.add(PUTCH);
        PUTCH.setSideEffect();

        libFunc.add(PUTF);
        PUTF.setSideEffect();

        ArrayList<IR_Value> params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",new IR_Pointer((i32Type))));
        GETARRAY.setParams(params);
        libFunc.add(GETARRAY);
        GETARRAY.setSideEffect();

        params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",new IR_Pointer((floatType))));
        GETFARRAY.setParams(params);
        libFunc.add(GETFARRAY);
        GETFARRAY.setSideEffect();

        params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",i32Type));
        PUTINT.setParams(params);
        libFunc.add(PUTINT);
        PUTINT.setSideEffect();

        params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",i32Type));
        PUTCH.setParams(params);
        libFunc.add(PUTCH);
        PUTCH.setSideEffect();

        params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",floatType));
        PUTFLOAT.setParams(params);
        libFunc.add(PUTFLOAT);
        PUTFLOAT.setSideEffect();

        params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",i32Type));
        params.add(new IR_Local_Value("reg",new IR_Pointer(i32Type)));
        PUTARRAY.setParams(params);
        libFunc.add(PUTARRAY);
        PUTARRAY.setSideEffect();

        params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",i32Type));
        params.add(new IR_Local_Value("reg",new IR_Pointer(floatType)));
        PUTFARRAY.setParams(params);
        libFunc.add(PUTFARRAY);
        PUTFARRAY.setSideEffect();

        params = new ArrayList<>();
        params.add(new IR_Local_Value("reg",new IR_Pointer(i32Type)));
        params.add(new IR_Local_Value("reg",i32Type));
        params.add(new IR_Local_Value("reg",i32Type));
        MEMSET.setParams(params);
        libFunc.add(MEMSET);
        MEMSET.setSideEffect();
    }
}
